/**
 * Created by Geniuslu on 11/20/15.
 */
var request = require('request');
var cheerio = require('cheerio');
var iconv = require('iconv-lite');
var async = require('async');
var urlencode = require('urlencode');
var xlsx = require('node-xlsx');


// json array to csv string
function json2string(obj) {
    if (obj instanceof Array && obj.length > 0) {
        return json2string(obj.shift()) + json2string(obj);
    }
    var str = '';
    for (var x in obj) {
        str = str + obj[x] + ','
    }
    str = str.substring(0, str.length - 1);
    str = str + '\n';
    return str;
}

function csv2json(csvStr, header) {
    var strArray = csvStr.split('\n');
    var objArray = [];
    for (var j = 0; j < strArray.length; j++) {
        if (strArray[j].length === 0) continue;
        var t = splitStr(strArray[j]);
        if (t.length === header.length) {
            var obj = {};
            for (var k = 0; k < header.length; k++) {
                obj[header[k]] = t[k];
            }
            objArray.push(obj);
        }
        else {
            throw Error('csv2json: Value and Header length not match!');
        }
    }
    return objArray;
}


// split string contains additional ',' inside
function splitStr(str) {
    if (str.length === 0) {
        throw new Error('Cannot split empty string!');
    }
    var strArray = str.split(',');
    var newStrArray = [];
    for (var j = 0; j < strArray.length; j++) {
        if (strArray[j].indexOf('"') === -1) {
            newStrArray.push(strArray[j]);
        }
        else if (strArray[j].indexOf('"') != strArray[j].lastIndexOf('"')) {
            newStrArray.push(strArray[j].replace(/"/g, ''));
        }
        else {
            var newStr = strArray[j] + ',' + strArray[j + 1];
            newStr = newStr.replace(/"/g, '');
            newStrArray.push(newStr);
            j++;
        }
    }
    return newStrArray;
}


// headSelector is string or array
// rowSelector and eleSelector are string
// eleSelector is children of rowSelector
function extractForm($, headSelector, rowSelector, eleSelector) {
    var headArray = [];
    if (headSelector instanceof Array) {
        headArray = headSelector;
    }
    else if (typeof headSelector === 'string') {
        var head = $(headSelector).children(eleSelector);
        for (var j = 0; j < head.length; j++) {
            headArray[j] = $(head[j]).text();
        }
    }
    else {
        throw new Error('Error type of headSelector');
    }

    var rows = $(rowSelector);
    var colNum = headArray.length;
    var rowNum = rows.length;
    var result = [];
    for (var j = 0; j < rowNum; j++) {
        var obj = {};
        for (var k = 0; k < colNum; k++) {
            var elements = $(rows[j]).children(eleSelector);
            obj[headArray[k]] = $(elements[k]).text().trim();
        }
        result.push(obj);
    }
    return result;
}


function date2strlocal(dateObj, format) {
    var yyyy = dateObj.getFullYear();
    var mm = dateObj.getMonth() + 1;
    var dd = dateObj.getDate();
    if (mm < 10) {
        mm = '0' + mm;
    }
    if (dd < 10) {
        dd = '0' + dd;
    }

    switch (format) {
        case 'yyyymmdd':
            return yyyy + '-' + mm + '-' + dd;
            break;
        case 'mmddyyyy':
            return mm + '-' + dd + '-' + yyyy;
            break;
        default :
            return yyyy + '-' + mm + '-' + dd;
    }
}

function date2strutc(dateObj, format) {
    var yyyy = dateObj.getUTCFullYear();
    var mm = dateObj.getUTCMonth() + 1;
    var dd = dateObj.getUTCDate();
    if (mm < 10) {
        mm = '0' + mm;
    }
    if (dd < 10) {
        dd = '0' + dd;
    }

    switch (format) {
        case 'yyyymmdd':
            return yyyy + '-' + mm + '-' + dd;
            break;
        case 'mmddyyyy':
            return mm + '-' + dd + '-' + yyyy;
            break;
        default :
            return yyyy + '-' + mm + '-' + dd;
    }
}

// timeStr = '1:23PM', '1:30'
// return '13:23', '01:30';
function adjustTimeFormat(timeStr) {
    if (timeStr.indexOf('pm') != -1 || timeStr.indexOf('PM') != -1) {
        timeStr = timeStr.match(/[0-9]{1,2}:[0-9]{1,2}/)[0];
        //timeStr = timeStr.toUpperCase().replace('PM', '').trim();
        var t = timeStr.split(':');
        t[0] = Number(t[0]) + 12;
        t[1] = Number(t[1]);

    }
    else if (timeStr.indexOf('am') != -1 || timeStr.indexOf('AM') != -1) {
        timeStr = timeStr.match(/[0-9]{1,2}:[0-9]{1,2}/)[0];
        //timeStr = timeStr.toUpperCase().replace('AM', '').trim();
        var t = timeStr.split(':');
        t[0] = Number(t[0]);
        t[1] = Number(t[1]);
    }
    else {
        timeStr = timeStr.trim();
        var t = timeStr.split(':');
        t[0] = Number(t[0]);
        t[1] = Number(t[1]);
    }
    if (isNaN(t[0]) || isNaN(t[1])) {
        return new Date().getUTCHours() + ':' + new Date().getUTCMinutes() + ' UTC';
    }

    if (t[0] >= 24) {
        t[0] = t[0] - 12;
    }
    else if (t[0] < 10) {
        t[0] = '0' + t[0];
    }
    if (t[1] < 10) {
        t[1] = '0' + t[1];
    }
    return t[0] + ':' + t[1];
}


// utcStr = 'yyyy-mm-dd hh:mm:ss UTC+0000' or any format of utctime (like milliseconds);
// tz = int or int.5; ex: -5, -6.5 etc;
function timeShift(utcStr, tz) {
    var time = new Date(new Date(utcStr).getTime() + tz * 3600000);
    var obj = {
        year: time.getUTCFullYear(),
        month: (time.getUTCMonth() + 1),
        date: time.getUTCDate(),
        hh: time.getUTCHours(),
        mm: time.getUTCMinutes()
    };
    for (var x in obj) {
        if (obj[x] < 10) {
            obj[x] = '0' + obj[x];
        }
    }


    time = obj.year + '-' + obj.month + '-' + obj.date + ' ' +
        obj.hh + ':' + obj.mm + ' UTC';

    var sign, hour, min;
    if (tz < 0) {
        sign = '-';
        tz = -tz;
    }
    else {
        sign = '+';
    }
    if (tz >= 10) {
        hour = '' + Math.floor(tz);
    }
    else {
        hour = '0' + Math.floor(tz);
    }
    if (Math.floor(tz) === tz) {
        min = '00';
    }
    else {
        min = '30';
    }
    return time + sign + hour + min;
}


// symbol = Outliers Symbol
// source = yahoo/google/investing
// return undefined if not find
function idxSymbolMap() {
    try {
    var fullData = getIdxFile().data;
    }
    catch (e) {
        throw new Error('Error getting idx file in idxSymbolMap');
    }

    this.getHisSymbolSets = function () {
        var symbolSets = {
            google: [],
            yahoo: [],
            ths: [],
            wscn: [],
            investing: [],
            sina: []
        };
        for (var j = 0; j < fullData.length; j++) {
            for (var k = 1; k < fullData[j].data.length; k++) {
                var t = fullData[j].data[k];
                if (t[7] != undefined && t[8] != undefined) { // sourced from google, use yahoo historical data
                    symbolSets.yahoo.push('.' + t[4]);
                }
                else if (t[11] != undefined) {  // sourced from sina, use sina historical data
                    symbolSets.sina.push('.' + t[4]);
                }
                else if (t[4] != undefined) {  // sourced from investing, use investing historical data
                    symbolSets.investing.push('.' + t[4]);
                }
            }
        }
        return symbolSets;
    };

    // str = 'active' | undefined
    // if str === 'active', only get symbols the local time of which is between 7AM-7PM
    this.getSymbolSets = function (str) {
        var symbolSets = {
            google: [],
            yahoo: [],
            ths: [],
            wscn: [],
            investing: [],
            sina: []
        };
        for (var j = 0; j < fullData.length; j++) {
            for (var k = 1; k < fullData[j].data.length; k++) {
                var t = fullData[j].data[k];
                if (str === 'active' && t[12] != undefined) { // has timezone
                    var time = new Date(new Date().getTime() + t[12] * 3600000);
                    if (time.getUTCHours() < 7 || time.getUTCHours() > 19) {
                        continue;
                    }
                }
                if (t[7] != undefined) {
                    symbolSets.google.push('.' + t[4]);
                }
                else if (t[11] != undefined) {
                    symbolSets.sina.push('.' + t[4]);
                }
                else if (t[4] != undefined) {
                    symbolSets.investing.push('.' + t[4]);
                }
                else if (t[8] != undefined) {
                    symbolSets.yahoo.push('.' + t[4]);
                }
                else if (t[9] != undefined) {
                    symbolSets.ths.push('.' + t[4]);
                }
                else if (t[10] != undefined) {
                    symbolSets.wscn.push('.' + t[4]);
                }
            }
        }
        return symbolSets;
    };

    this.getSymbolList = function () {
        var temp = this.getHisSymbolSets();
        var list = [];
        for (var x in temp) {
            for (var j = 0; j < temp[x].length; j++) {
                list.push(temp[x][j]);
            }
        }
        return list;
    };

    this.search = function (symbol) {
        symbol = symbol.replace('.', '');
        for (var j = 0; j < fullData.length; j++) {
            var data = fullData[j].data;
            for (var k = 1; k < data.length; k++) {
                var row = data[k];
                if (symbol === row[4]) {
                    return {
                        country: row[0],
                        namecn: row[2],
                        nameen: row[3],
                        symbol: '.' + symbol,
                        investingID: row[5],
                        investingURL: row[6],
                        googleSymbol: row[7],
                        yahooSymbol: row[8],
                        thsSymbol: row[9],
                        sinaSymbol: row[11],
                        tz: row[12]
                    }
                }
            }
        }
        return undefined;
    }
}


// getTimeZone from Baidu
function getTimeZone(countries, callback) {
    if (countries instanceof Array) {
        async.concat(countries, function (item, callback) {
            getTimeZone(item, function (err, date) {
                if (err) return callback(err, null);
                callback(null, date);
            });
        }, function (err, dateArray) {
            if (err) return callback(err, null);
            callback(null, dateArray);
        });
    }
    else {
        if (typeof countries === 'string') {
            url = 'https://www.baidu.com/s?ie=utf-8&f=8&rsv_bp=1&rsv_idx=1&tn=baidu&wd=' +
                urlencode(countries + ' 时区');
        }
        var options = {
            url: url,
            timeout: 20000,
            headers: {
                'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_10_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/46.0.2490.86 Safari/537.36'
            }
        };
        request(options, function (err, res, body) {
            if (err) return callback(err, null);
            try {
                var $ = cheerio.load(body);
                var time = $('span.op_exactqa_timer').text()
                    .replace(/[年月]/g, '/')
                    .replace(/[日]/, '')
                    .split(' ');
                var dateStr = new Date(time[0]).toDateString();
                var tzShift = Math.round((new Date(time + ' UTC') - new Date()) / 360000) / 10;
                var tz = tzShift;
                if (tz >= 0 && tz < 10) {
                    tz = ' GMT+0' + tz + '00';
                }
                else if (tz >= 10) {
                    tz = ' GMT+' + tz + '00';
                }
                else if (tz < 0 && tz > -10) {
                    tz = ' GMT-0' + (-tz) + '00';
                }
                else {
                    tz = ' GMT-' + (-tz) + '00';
                }
            }
            catch (e) {
                console.error(e);
                return callback(null, []);
            }


            var local = dateStr + ' ' + time[1] + tz;
            var utc = (new Date().toUTCString() + '+0000').replace(/,/g, '');
            if (isNaN(tzShift)) {
                local = undefined;
                utc = undefined;
                tzShift = undefined;
            }

            callback(null, [{
                country: countries,
                local: local,
                utc: utc,
                tz: tzShift
            }]);
        })
    }
}


function getIdxFile() {
    //var cwd = process.cwd();
    //var mainDir = cwd.match(/.*Outliers_Aggregator/),
    //    idxFilePath,
    //    currPath = '.';
    //console.log(cwd);
    //if (mainDir === null) {
    //    throw new Error('Only Scripts in Outliers_Aggregator can real this File!');
    //}
    //mainDir = mainDir[0];
    //idxFilePath = mainDir + '/' + 'outliers_modules/Quotes/lib';
    //
    //cwd = cwd.replace(mainDir, '').split('/');
    //idxFilePath = idxFilePath.replace(mainDir, '').split('/');
    //
    //while (cwd.length > 0 && idxFilePath.length > 0 && cwd[0] === idxFilePath[0]) {
    //    cwd.shift();
    //    idxFilePath.shift();
    //}
    //for (var j = 0; j < cwd.length; j++) {
    //    currPath = '../' + currPath;
    //}
    //for (var j = 0; j < idxFilePath.length; j++) {
    //    currPath = currPath + '/' + idxFilePath[j];
    //}
    //currPath = currPath + '/idxFull.xlsx';
    return {
        data: xlsx.parse('./idxFull.xlsx'),
        path: './idxFull.xlsx'
    };
}

exports.json2string = json2string;
exports.csv2json = csv2json;
exports.splitStr = splitStr;

exports.timeShift = timeShift;
exports.adjustTimeFormat = adjustTimeFormat;
exports.date2strlocal = date2strlocal;
exports.date2strutc = date2strutc;

exports.extractFrom = extractForm;
exports.idxSymbolMap = idxSymbolMap;
exports.getTimeZone = getTimeZone;
exports.getIdxFile = getIdxFile;


